package com.ocom.mqsimple.business.consumedetail;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ocom.common.dto.GetMngUserFormDto;
import com.ocom.common.entity.card.EntityXfAccountType;
import com.ocom.common.entity.card.EntityXfMealInfo;
import com.ocom.common.entity.master.EntityFileDownloadInfo;
import com.ocom.common.enums.*;
import com.ocom.common.request.web.ConsumeDetailRequest;
import com.ocom.common.resp.Result;
import com.ocom.common.utils.RandomUtil;
import com.ocom.common.utils.csv.ZipUtil;
import com.ocom.common.vo.KVSelectedVo;
import com.ocom.common.vo.simple.ConsumeDetailVo;
import com.ocom.mqsimple.config.ConsumeDetailExportConfig;
import com.ocom.mqsimple.feign.*;
import com.ocom.redis.util.RedisUtil;
import com.rabbitmq.client.Channel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.messaging.handler.annotation.Headers;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


@Component
public class ConsumeDetailExportConsumerMqListener {

    private Logger logger = LoggerFactory.getLogger(ConsumeDetailExportConsumerMqListener.class);

    @Value("${save.path}")
    String excelPath;

    @Resource
    RedisUtil redisUtil;

    @Autowired
    private PayConfigClientService payConfigClientService;

    @Autowired
    AccessClientService accessClientService;

    @Autowired
    private XfRecordFeign xfRecordFeign;

    @Autowired
    MinioClientService minioClientService;

    @Autowired
    private HumanClientService humanClientService;

    private Map<Integer, String> initAccTypeMap = new HashMap();

    /**
     * 把该监听命名为A监听
     * 监听队列  远程服务支付接口发送消息  该队列会接受
     * [message, headers, channel]
     *
     * @return {@link }
     * @throws
     */
    @RabbitListener(queues = ConsumeDetailExportConfig.CONSUME_DETAIL_EXPORT_QUEUE)
    public void process(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception {
        logger.info("ConsumeDetailExport 消费中");
        logger.info("message【{}】", message);
        String humanMessage = new String(message.getBody(), StandardCharsets.UTF_8);
        //TODO 数据库操作样例
        logger.info("ConsumeDetailExport-start:【{}】", humanMessage);
        logger.info("System.currentTimeMilli早呀早呀早呀s()【{}】", System.currentTimeMillis());

        Object requestRedis = redisUtil.get(humanMessage);

        if (requestRedis == null) {
            logger.info("ConsumeDetailExport-error:redis为空异常");
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }

        ConsumeDetailRequest request = JSON.parseObject(JSON.toJSONString(requestRedis), ConsumeDetailRequest.class);

        JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(request));
        jsonObject.put("id","0");
        EntityFileDownloadInfo entityFileDownloadInfo = new EntityFileDownloadInfo();
        entityFileDownloadInfo.setId(request.getId().longValue());

        //获取异常-跳过  失败处理
        Result getMealConsumeReports = payConfigClientService.getMealConsumeReports(request.getComId());
        if (getMealConsumeReports.getCode() != 200) {
            logger.info("ConsumeDetailExport-error: 【{}】", getMealConsumeReports);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

            //下载失败
            entityFileDownloadInfo.setStatus(2l);
            entityFileDownloadInfo.setRemark("redis空");
            accessClientService.UpdateFileStatus(entityFileDownloadInfo);
            return;
        }

//        String filePath = excelPath + RandomUtil.getRandomString(32) + ".xlsx";
//        File file = new File(filePath);

        String randStr = RandomUtil.getRandomString(32);


        long startTime = System.currentTimeMillis();
        // 设置表格头
//        Object[] head = {"序号","小说名称","作者","出版日期"};
//        List<Object> headList = Arrays.asList(head);
        List<KVSelectedVo> kvSelectedVos = ExcelDownLoadEnum.getValueByKey("/consume/report/detail");
        List<Object> headList = kvSelectedVos.stream().map(KVSelectedVo -> KVSelectedVo.getName()).collect(Collectors.toList());
        ;


        GetMngUserFormDto getMngUserFormDto = new GetMngUserFormDto();
        getMngUserFormDto.setUserId(request.getUserId());
        getMngUserFormDto.setPage("/consume/report/detail");
        getMngUserFormDto.setType(1);

        Result getMngUserFormSelectedResult = humanClientService.getMngUserFormSelected(getMngUserFormDto);
        if (200 == getMngUserFormSelectedResult.getCode()) {
            kvSelectedVos = JSON.parseArray(JSON.toJSONString(getMngUserFormSelectedResult.getData()), KVSelectedVo.class).stream().filter(o -> o.getSelect()).collect(Collectors.toList());
            headList = kvSelectedVos.stream().map(KVSelectedVo -> KVSelectedVo.getName()).collect(Collectors.toList());
            ;
        }

        // 设置数据

        jsonObject.put("page", 1);
        jsonObject.put("size", 1);
        Result result = xfRecordFeign.getConsumeDetail(jsonObject);

        if (result.getCode() != 200) {
            logger.info("ConsumeDetailExport-error: 【{}】", getMealConsumeReports);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            //下载失败
            entityFileDownloadInfo.setStatus(2l);
            entityFileDownloadInfo.setRemark("获取食堂消费汇总报表- 获取异常");
            accessClientService.UpdateFileStatus(entityFileDownloadInfo);
            return;
        }

        ConsumeDetailVo mjRecords = JSON.parseObject(JSON.toJSONString(result.getData()), ConsumeDetailVo.class);
        //下载失败 无数据
        if (mjRecords.getTotal() <= 0) {
            //下载失败
            entityFileDownloadInfo.setStatus(2l);
            entityFileDownloadInfo.setRemark("无有效记录");
            accessClientService.UpdateFileStatus(entityFileDownloadInfo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }

        int listCount = mjRecords.getTotal();
        //导出6万以上数据。。。
        int pageSize = 100000;//设置每一个excel文件导出的数量   500的倍数  1次请求500条
        int quotient = listCount / pageSize + (listCount % pageSize > 0 ? 1 : 0);//循环次数
        List<File> srcfile = new ArrayList<File>();
        int oneFilePages = pageSize / 500;
        String downloadFilePath = excelPath + randStr + File.separator;

        //采集单位基础信息用于替换，例如部门名，账户类型等
        initComMap(request.getComId());

        try {

            Result mealNameInfo = payConfigClientService.GetMealNameInfo(request.getComId());
            Map<String,String> mealNameMap = new HashMap<>();
            if (200 == mealNameInfo.getCode()) {
                Map<String, Object> mealNameInfoData = JSON.parseObject(JSON.toJSONString(mealNameInfo.getData()), HashMap.class);
                List<EntityXfMealInfo> entityXfMealInfos = JSON.parseArray(JSON.toJSONString(mealNameInfoData.get("list")), EntityXfMealInfo.class);
                mealNameMap = entityXfMealInfos.stream().collect(Collectors.toMap(e -> e.getMealNo(),
                        e -> e.getMealName(),
                        (e1, e2) -> e1));
            }



            for (int i = 0; i < quotient; i++) {
                int startPage = oneFilePages * i + 1;
                int endPage = oneFilePages + startPage - 1;

                if (listCount % pageSize > 0 && (i + 1) == quotient) {
//                    System.out.println("最后一页");
                    int lastTotal = listCount - pageSize * (i);
                    if (lastTotal <= 500) {
                        endPage = startPage;
                    } else {
                        endPage = startPage + lastTotal / 500;
                    }
                }

//                System.out.println("startPage:" + startPage);
//                System.out.println("endPage" + endPage);

                // 导出文件名称
                String fileName = String.valueOf(i);
                // 导出CSV文件
                File csvFile = createCSVFile(headList, downloadFilePath, fileName, jsonObject, startPage, endPage,kvSelectedVos,mealNameMap);
                srcfile.add(csvFile);
            }

            ZipUtil.zipFiles(srcfile, new File(excelPath + randStr + "download.zip"));
            long endTime = System.currentTimeMillis();
 //           System.out.println("分批CSV导出" + (endTime - startTime));
        } catch (Exception e) {
            //下载失败
            logger.info("【ConsumeDetailExport】-error: 【{}】", e);
            entityFileDownloadInfo.setStatus(2l);
            entityFileDownloadInfo.setRemark("导出异常");
            accessClientService.UpdateFileStatus(entityFileDownloadInfo);
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        } finally {
            try {
                ZipUtil.dropFolderOrFile(new File(downloadFilePath));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        //      channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);

        File file1 = null;
        try {
            file1 = new File(excelPath + randStr + "download.zip");
            Result result3 = minioClientService.upload(file1);
            if (result3.getCode() != 200) {
                //更改状态 失败
                entityFileDownloadInfo.setStatus(2l);
                accessClientService.UpdateFileStatus(entityFileDownloadInfo);
            } else {
                JSONObject jsonObject1 = JSON.parseObject(JSON.toJSONString(result3.getData()));
                entityFileDownloadInfo.setStatus(1l);
                entityFileDownloadInfo.setUrl(jsonObject1.getString("imageUrl"));
                logger.info("entityFileDownloadInfo 【{}】", entityFileDownloadInfo);
                accessClientService.UpdateFileStatus(entityFileDownloadInfo);
            }
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            logger.info("ConsumeDetailExport-end:【{}】", humanMessage);
            logger.info("System.currentTimeMillis()【{}】", System.currentTimeMillis());
        } catch (Exception e) {
            logger.error("STUDENT_EXCEL_QUEUE 人员导出IO异常 【{}】", e);
            channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);//继续消费该消息
        } finally {
            try {
                file1.delete();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }


    }

    private void initComMap(Long comId){
        try {
            Result getXFAccountType = payConfigClientService.GetXFAccountType(comId);
            if (200 == getXFAccountType.getCode()) {
                List<EntityXfAccountType> xfAccountTypeList = JSON.parseArray(JSON.toJSONString(getXFAccountType.getData()), EntityXfAccountType.class);
                initAccTypeMap = xfAccountTypeList.stream().collect(Collectors.toMap(e -> e.getAccTypeId(), e -> e.getAccTypeName()));
            }

        }catch (Exception e){
            e.printStackTrace();
        }

    }

    /**
     * 把该监听命名为B监听
     * 监听死信队列  远程服务支付接口发送消息  该队列会接受
     * [message, headers, channel]
     *
     * @return {@link }
     * @throws
     */
    @RabbitListener(queues = ConsumeDetailExportConfig.CONSUME_DETAIL_EXPORT_DIE_QUEUE)
    public void processForDie(Message message, @Headers Map<String, Object> headers, Channel channel) throws Exception {
        //死信队列放弃
        logger.info("消费明细导出-死信队列放弃 ， 【{}】", message);
        String humanMessage = new String(message.getBody(), StandardCharsets.UTF_8);
        Object requestRedis = redisUtil.get(humanMessage);
        redisUtil.dels(humanMessage);

        if (requestRedis == null) {
            logger.info("ConsumeDetailExport-processForDie:redis为空异常");
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            return;
        }

        ConsumeDetailRequest request = JSON.parseObject(JSON.toJSONString(requestRedis), ConsumeDetailRequest.class);

        EntityFileDownloadInfo entityFileDownloadInfo = new EntityFileDownloadInfo();
        entityFileDownloadInfo.setId(request.getId().longValue());

        //获取异常-跳过  失败处理

        //下载失败
        entityFileDownloadInfo.setStatus(2l);
        entityFileDownloadInfo.setRemark("死信队列-导出失败");
        accessClientService.UpdateFileStatus(entityFileDownloadInfo);

        channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
    }

    private Object setCellValue(JSONObject mjRecord, KVSelectedVo vo) {
        try {
            switch (vo.getField()) {
                case "chargeBalance":
                    return  (double) (mjRecord.getInteger("chargeBalance") / 100.0);
                case "chargeMoney":
                    return (double) (mjRecord.getFloatValue("chargeMoney") / 100.0);
                case "consMoney":
                    return (double) (mjRecord.getFloatValue("consMoney") / 100.0);
                case "consState":
                    return ConsStateEnum.getValueByKey(mjRecord.getIntValue("consState"));
                case "opType":
                    return OpTypeEnum.getName(mjRecord.getIntValue("opType"));
                case "opTypeDiff":
                    return OpTypeDiffEnum.getName(mjRecord.getIntValue("opTypeDiff"));
                case "payAmount":
                    return (double) (mjRecord.getFloatValue("payAmount") / 100.0);
                case "payState":
                    return PayStateEnum.getValueByKey(mjRecord.getIntValue("payState"));
                case "realMoney":
                    return (double) (mjRecord.getFloatValue("realMoney") / 100.0);
                case "subBalance":
                    return (double) (mjRecord.getFloatValue("subBalance") / 100.0);
                case "subMoney":
                    return (double) (mjRecord.getFloatValue("subMoney") / 100.0);
                case "subsidy":
                    return (double) (mjRecord.getFloatValue("subsidy") / 100.0);
                case "channelFee":
                    return (double) (mjRecord.getFloatValue("channelFee") / 100.0);
                case "discount":
                    return (double) (mjRecord.getFloatValue("discount") / 100.0);
                case "accTypeId":
                    return (String) initAccTypeMap.getOrDefault(mjRecord.getIntValue("accTypeId"), "");
                case "consType":
                    return 1 == mjRecord.getIntValue("consType") ? "饭堂" : "商店";
                default:
                    return null == mjRecord.getString(vo.getField()) ? "" : mjRecord.getString(vo.getField());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }


    /**
     * CSV文件列分隔符
     */
    private static final String CSV_COLUMN_SEPARATOR = ",";

    /**
     * CSV文件列分隔符
     */
    private static final String CSV_RN = "\r\n";

    /**
     * CSV文件生成方法
     *
     * @param head
     * @param outPutPath
     * @param filename
     * @return
     */
    public File createCSVFile(List<Object> head, String outPutPath, String filename, JSONObject jsonObject, int startPage, int endPage,List<KVSelectedVo> kvSelectedVos,Map<String,String> mealNameMap) throws Exception {


        File csvFile = null;
        BufferedWriter csvWtriter = null;
        try {
            csvFile = new File(outPutPath + File.separator + filename + ".csv");
            File parent = csvFile.getParentFile();
            if (parent != null && !parent.exists()) {
                parent.mkdirs();
            }
            csvFile.createNewFile();

            // GB2312使正确读取分隔符","
            csvWtriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(csvFile), "UTF-8"), 1024);
            // 写入文件头部
            writeRow(head, csvWtriter,null);

            // 写入文件内容
//            for (List<Object> row : dataList) {
//                writeRow(row, csvWtriter);
//            }

            for (int i = startPage; i <= endPage; i++) {
                jsonObject.put("page", i);
                jsonObject.put("size", 500);

//                try {
                    Result result1 = xfRecordFeign.getConsumeDetail(jsonObject);
                    ConsumeDetailVo mjRecords1 = JSON.parseObject(JSON.toJSONString(result1.getData()), ConsumeDetailVo.class);
//                    System.out.println("起始页数:" + startPage + "，截止页数:" + endPage + "，当前页数:" + i);
                    if (result1.getCode() != 200 || mjRecords1 == null) {
//                        System.out.println("参数:【{" + jsonObject + "}】回异常:【{" + result1 + "}】");
                        throw new Exception("查询异常 返回："+result1);
                   //     continue;
                    }
                    if (mjRecords1 != null && mjRecords1.getList() != null && mjRecords1.getList().size() > 0) {
                        for (Object o : mjRecords1.getList()) {

                            JSONObject mjRecord = JSON.parseObject(JSON.toJSONString(o));

                            Map<Integer,Boolean> booleanMap =new HashMap<>();

                            List<Object> row = new ArrayList<>();
                            for (int m = 0; m < kvSelectedVos.size(); m++) {

                                if (kvSelectedVos.get(m).getField().equals("mealName")) {
                                    booleanMap.put(m, false);
                                    String mealNo = mjRecord.getString("mealNo");
                                    if (mealNameMap.containsKey(mealNo)) {
                                        row.add(mealNameMap.get(mealNo));
                                    } else {
                                        row.add("");
                                    }
                                    continue;
                                }
                                if (kvSelectedVos.get(m).getField().equals("countPay")) {
                                    booleanMap.put(m, true);
                                    Integer ifRefund = mjRecord.getInteger("ifRefund");
                                    Integer consState = mjRecord.getInteger("consState");
                                    if (consState == 1 || consState == 3 || consState == 5) {
                                        row.add(1);
                                    } else {
                                        row.add(-1);
                                    }
                                    continue;
                                }


                                row.add(setCellValue(mjRecord, kvSelectedVos.get(m)));

                                if(kvSelectedVos.get(m).getField().equals("chargeBalance")
                                || kvSelectedVos.get(m).getField().equals("chargeMoney")
                                || kvSelectedVos.get(m).getField().equals("consMoney")
                                || kvSelectedVos.get(m).getField().equals("payAmount")
                                || kvSelectedVos.get(m).getField().equals("realMoney")
                                || kvSelectedVos.get(m).getField().equals("subBalance")
                                || kvSelectedVos.get(m).getField().equals("subMoney")
                                || kvSelectedVos.get(m).getField().equals("subsidy")
                                || kvSelectedVos.get(m).getField().equals("channelFee")
                                || kvSelectedVos.get(m).getField().equals("discount")
                                ){
                                    booleanMap.put(m,true);
                                }else{
                                    booleanMap.put(m,false);
                                }
                            }
                            writeRow(row, csvWtriter,booleanMap);
                        }
                    }

//                } catch (Exception e) {
//                    e.printStackTrace();
//                    System.out.println("起始页数:" + startPage + "，截止页数:" + endPage + "，当前异常页数:" + i);
//                }

            }
            csvWtriter.flush();
        } catch (Exception e) {
//            e.printStackTrace();
            logger.error("【ConsumeDetailExport】 【异常】 【{}】",e);
            throw new Exception("查询异常");
        } finally {
            try {
                csvWtriter.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return csvFile;
    }

    /**
     * 写一行数据方法
     *
     * @param row
     * @param csvWriter
     * @param  booleanMap 是否数字 不加字符号
     * @throws IOException
     */
    private void writeRow(List<Object> row, BufferedWriter csvWriter, Map<Integer,Boolean> booleanMap) throws IOException {
        // 写入文件头部

        for (int m = 0; m < row.size(); m++) {
            Object data = row.get(m);
            StringBuffer buf = new StringBuffer();
 //           String  rowStr = buf.append(data).append(",").toString();
            String rowStr = null;
            if(booleanMap!=null &&  booleanMap.containsKey(m) && booleanMap.get(m)==true){
                rowStr = buf.append(data).append(",").toString();
            }else{
                rowStr =  buf.append("\"").append(data).append("\t\",").toString();
            }
            csvWriter.write(rowStr);
        }
        csvWriter.newLine();
    }

}
